Skip to content

The engine gaps left open after the SDK batch (2026.9.12.2) - #619

Merged
Sunrisepeak merged 6 commits into
mainfrom
design/engine-gaps-2026-09-12
Sep 11, 2026
Merged

The engine gaps left open after the SDK batch (2026.9.12.2)#619
Sunrisepeak merged 6 commits into
mainfrom
design/engine-gaps-2026-09-12

Conversation

@Sunrisepeak

@Sunrisepeak Sunrisepeak commented Sep 11, 2026

Copy link
Copy Markdown
Member

Implements .agents/docs/2026-09-12-engine-gaps-after-the-sdk-batch.md, tasks M1 to M9, as one change carrying version 2026.9.12.2. 2026.9.12.1 (#617) was not published, and its CHANGELOG entry is folded into this version. The record's §11 lists the corrections made during implementation.

Changes

#618: a Windows GUI executable, declared on its target

  • [targets.<name>] windows_subsystem = "console" | "windows" and windows_entry = "main" | "wmain" | "WinMain" | "wWinMain".
  • The emitter renders them onto the declaring executable's own link edge only:
    • on the MSVC ABI, /SUBSYSTEM: together with /ENTRY:<entry>CRTStartup whenever either key differs from its default, spelled with -Wl, under a GNU-style driver;
    • on the GNU ABI, -mwindows and -municode;
    • nothing off PE.
  • The ABI predicate is pe_msvc_abi, extracted from pe_link_flag, so the import library and the subsystem address the same linker.
  • Both keys are refused on a library target, naming the target and the key. An unknown value is refused, naming the accepted values.
  • The warning for an unsupported per-target key now lists the parser's own key table; the hand-written copy had omitted exports.
  • Build-program protocol 10 adds mcpp:windows-subsystem=<target>:<value> and mcpp:windows-entry=<target>:<value>, with a new Scope::TargetLink. target_directive_error checks them against the manifest before apply, on both the run path and the cache-hit path.

T1: the fast path compares the toolchain request

  • Measured before the fix: after a gcc build, mcpp build --toolchain llvm@22.1.8 printed Finished dev in 0.00s and left the gcc artefact in place.
  • The build cache records toolchain= (the --toolchain override and [toolchain] default), and both the build and run fast paths compare it.
  • The named set is the target triple, profile, cache mode, features and toolchain request. --offline, --locked and --jobs do not change what resolution chooses, so they are not compared.
  • Revert probe: tests/e2e/645 fails with the 2026.9.11.4 binary at exactly that step.
  • The fast path is taken only for ELF artefacts, and that predates this change (feat: converge template identity, runtime contracts, and release state #400): try_fast_build requires an ELF runtime-validation verdict for every artefact. On macOS and Windows CI, 645's control therefore found an unchanged second build resolving the toolchain. The test compares the toolchain= lines the two builds record: identical lines report the host as not measured, and differing lines fail. The record's §11 notes the limitation as outside this change.

#615: runtime.deploy

  • [runtime] deploy = [{ from, to }] in mcpp.toml, and runtime.deploy in descriptors. Destinations are bin/<to>/<file>, and "." means beside the executable.
  • One path rule applies to both parsers; each malformed entry is refused naming its index. Two sources for one destination are refused naming the destination.
  • deploy is a key of its own because an older descriptor reader does not terminate on { inside deploy_files.
  • The TOML array-of-tables allowlist gains runtime.deploy. The unit test written for the key reported that gap.
  • mcpp pack stages the files of both deploy_files and deploy at the same relative path beside the packed executable, in both the ELF and PE layouts. Before this, pack read neither list.

[target.<selector>.abi] threads and requires_abi (record §5.2)

  • A typed graph-wide switch, rendered as -pthread on targets that are neither PE nor freestanding. It is applied to the root's dialect_cxxflags, cflags and ldflags and to every dependency's cflags, so it reaches the std module prebuild, the scan, every translation unit, the link and the dependency cache key.
  • requires_abi = { threads = true } on a package or a feature is refused before compilation when the root does not state the switch, naming the package, the feature and the table that satisfies it.
  • A dependency's own [target.<selector>.abi] is reported (abi/dependency-table) and ignored.

#613: the install hook's environment

  • A dependency's install hook receives MCPP_TARGET and its three segments, and MCPP_COMPILER and MCPP_CXX_STDLIB present and empty, so a hook never reads a value inherited from a parent process.
  • The toolchain values are empty because prepare resolves the toolchain after the dependency graph: a package in the graph may supply a target-side layer. tests/e2e/648 measured this, and the record's §11 states that neither §2.4 criterion holds as written.
  • install_hook_env computes the six values, and contract_env now takes them from it, in the same order, so no build program's re-run key changes.
  • docs/06, docs/22 and docs/32 state the requires = ["mcpp:c++-abi=<stdlib>"] recipe for a source-built static package; the refusal names both implementations once the toolchain is resolved.

#614: one decision for an xlings invocation's environment

  • invocation_env decides the environment: global mode is an absent XLINGS_PROJECT_DIR.
  • POSIX renders the decision into the command prefix, which is byte-identical to the previous strings.
  • On Windows, ScopedInvocationEnv applies the project scope for the lifetime of each runner function and restores the prior value. _putenv_s(key, "") already meant removal there; what was wrong was the lifetime.
  • The POSIX install_packages fallback no longer spells global mode by hand.
  • platform::env::unset is explicit.
  • A failed install appends xlings' own error-level stderr lines (at most the last 20) to mcpp's diagnostic, through stderr_error_tail.

#609 and G1

Tests

Unit tests:

  • test_manifest: windows keys, runtime.deploy, requires_abi and the abi table, the G1 refusal, and the generated key list.
  • test_ninja_backend: every row of the subsystem rendering tables, plus a graph test showing the flag reaches only the declaring executable.
  • test_build_directives: scope, refusals and cache round trip of the new directives.
  • test_runtime_contract: the descriptor deploy reader.
  • test_xlings: invocation_env, the restore guarantee and the POSIX prefix.
  • test_install_hook_env: a new file.

All 112 unit test binaries pass locally.

End-to-end tests:

  • 642 (native Windows, MSVC ABI) and 643 (mingw-cross, run under wine) read the PE Subsystem field from the image bytes and check that a static constructor runs before main.
  • 644: the keys are inert and byte-identical on ELF, and the refusals fire.
  • 645: the A-B-A fast-path request test, the mcpp run leg, and the machine-default leg (newest other installed version, checked in the artefact); reported as not measured on hosts whose fast path declines for another reason.
  • 646: runtime.deploy placement, the test binary layout, the collision and the escape refusal.
  • 647: the abi switch, its refusals, the dependency table, and the host inertness of a table scoped to emscripten.
  • 648: a local-index install hook compiles in what it saw: the build's target, and empty toolchain names despite values exported by the parent.
  • 649: mcpp pack carries both lists.

Ecosystem follow-ups (after release)

  • E1, openkal-emscripten: the README correction, requires_abi on the threads feature, and the CI engine pin.
  • E2, mcpp-index: compat.mysql-connector-cpp declares requires = ["mcpp:c++-abi=libstdc++"], checked against the index floor.

Issues #564 to #618 checked against main at c688fca: six were fixed and are
closed, two are not engine work, and four remain. Their designs, together with
the two gaps the 2026-09-11 record left open, of which one turns out to be
misdiagnosed: the per-target tool declaration exists under
[target.<selector>.xlings.workspace], measured on linux-x86_64.

For review before implementation.
…ask list

The four review questions are answered: windows_subsystem takes "windows",
windows_entry ships now, a resolve-time refusal is enough for #613, and the
graph-wide switch is an abi table.

Measuring the plan corrected it three times. The #613 refusal already exists as
requires = ["mcpp:c++-abi=libstdc++"]. The whole-graph channel G2 asked for is
dialect_cxxflags: -pthread there links the openkal-emscripten threads feature,
while the same flag in cxxflags reproduces the recorded error. And a new
defect, T1: mcpp build --toolchain llvm@22.1.8 after a gcc build replays the
gcc build in 0.00s.
Implements .agents/docs/2026-09-12-engine-gaps-after-the-sdk-batch.md (M1-M9).

- #618: `[targets.<name>] windows_subsystem` and `windows_entry`, rendered per
  ABI onto the declaring executable's own link (`/SUBSYSTEM:` with the CRT
  `/ENTRY:` symbol on MSVC, `-mwindows`/`-municode` on GNU), inert off PE, and
  refused on library targets. Build-program protocol 10 adds the
  `windows-subsystem` and `windows-entry` directives for a named target.
- T1: the fast path compares the toolchain request (`--toolchain`,
  `MCPP_TOOLCHAIN`, and `[toolchain] default`); a build asking for another
  toolchain no longer replays the previous one.
- #615: `runtime.deploy = [{ from, to }]` places runtime files in a directory
  relative to the executable, in mcpp.toml and in descriptors, and `mcpp pack`
  carries both `deploy_files` and `deploy` at the same relative paths.
- `[target.<selector>.abi] threads` as a typed graph-wide switch, with
  `requires_abi` on a package or feature refused before compilation when the
  root does not state it.
- #613: a dependency's install hook receives MCPP_COMPILER, MCPP_CXX_STDLIB and
  the target names, computed by the function the build-program environment
  uses; docs state the `mcpp:c++-abi` recipe for source-built static packages.
- #614: one function decides an xlings invocation's environment, the Windows
  project scope is restored after each invocation, and xlings' own error lines
  follow a failed install.
- #609 and G1: a documented toolchain hazard, and the refusal that names
  `[target.<selector>.xlings.workspace]`, with examples/13 using it.
…correct the hook environment

Windows CI (clang 20.1.7, x86_64-pc-windows-msvc) crashed compiling
directives.cppm. The change introduced constructs of the kind src/pack/pack.cppm
records crashing that toolchain: pointers to members of an imported type and a
std::span over an exported constexpr array, plus optional-bearing types in an
exported interface. They are replaced by plain code: `windows_choice_problem`
states the accepted values once for both parsers, the directive check and apply
are per field, `invocation_env` returns a plain struct, ScopedInvocationEnv
keeps plain members, and the hook scope uses six ScopedEnv locals.

tests/e2e/648 measured that a dependency's install hook runs before the
toolchain is resolved, because prepare resolves it after the dependency graph.
The hook therefore receives the build's target and empty toolchain names; the
code comment, docs/22, docs/32, the CHANGELOG and the design record now state
this, and 648 asserts it, including that inherited values do not leak through.

Test fixes: 645 resolves its helper directory before changing directory and
drives `mcpp run` through MCPP_TOOLCHAIN; 647 writes each manifest to its own
package directory.
…ersion

The first other version found was gcc 13.3.0, which rejects -fmodules, so the leg measured the compiler rather than the fast path. Measured after the change: build A-B-A, run A-B-A, and gcc@16.1.0 -> gcc@15.1.0 with the artefact's version string, all passing.
…rted, not failed

macOS and Windows CI found an unchanged second build resolving the toolchain: try_fast_build requires an ELF runtime-validation verdict for every artefact, which Mach-O and PE never record (#400). The control now compares the toolchain= lines the two builds record; identical lines report the host as not measured and print the entry, differing lines fail. The design record notes the ELF-only fast path as outside this change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants